//-------------------------------------------------------------------------------------
// <copyright file="BizTalkInstallation.cs" company="Microsoft">
//      Copyright (c) 2003 Microsoft Corporation. All rights reserved.
//      THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
//      EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
//      WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// </copyright>
// <summary>
//      Represents an installation of a BizTalk 2004 server.
// </summary>  
//-------------------------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.BizTalk2004.Configuration
{
    #region Using directives

    using System;
    using System.Globalization;
    using Microsoft.Win32;

    #endregion

    /// <summary>
    /// Represents an installation of a BizTalk 2004 server.
    /// </summary>
    internal class BizTalkInstallation
    {
        #region Member Variables

        private BtsCatalogExplorer catalogExplorer = new BtsCatalogExplorer();

        private string server;
        private string database;
        private string groupName;

        private BizTalkAssemblyCollection deployedAssemblies;
        private HostCollection hosts;
        private ProtocolCollection protocols;
        private ReceivePortCollection receivePorts;
        private SendPortGroupCollection sendPortGroups;
        private SendPortCollection sendPorts;
        private CertificateInfoCollection certificates;
        private PartyCollection parties;

        #endregion

        #region Constructors

        /// <summary>
        /// Private only as new installations cannot be created - this constructor is provided for the Load
        /// method so that it can create instances of this class.
        /// </summary>
        private BizTalkInstallation()
        {
        }

        #endregion

        #region Properties
        
        /// <summary>
        /// Gets or sets the name of the BizTalk management database that this object belongs to.
        /// </summary>
        /// <value>
        /// The name of the BizTalk management database that this object belongs to.
        /// </value>
        public string Database
        {
            get
            {
                return this.database;
            }

            set
            {
                this.database = value;
            }
        }

        /// <summary>
        /// Gets the collection of deployed assemblies.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkAssemblyCollection"/>.
        /// </value>
        public BizTalkAssemblyCollection DeployedAssemblies
        {
            get
            {
                if (this.deployedAssemblies == null)
                {
                    this.deployedAssemblies = new BizTalkAssemblyCollection();
                    foreach (BtsAssembly actualAssembly in this.catalogExplorer.Assemblies)
                    {
                        BizTalkAssembly configAssembly = BizTalkAssembly.Load(this, actualAssembly.DisplayName);
                        this.deployedAssemblies.Add(configAssembly);
                    }
                }
                return this.deployedAssemblies;
            }
        }

        /// <summary>
        /// Gets the collection of hosts.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.HostCollection"/>.
        /// </value>
        public HostCollection Hosts
        {
            get
            {
                if (this.hosts == null)
                {
                    this.hosts = new HostCollection();
                    foreach (BtsHost actualHost in this.catalogExplorer.Hosts)
                    {
                        Host configHost = Host.Load(this, actualHost.Name);
                        this.hosts.Add(configHost);
                    }
                }
                return this.hosts;
            }
        }

        /// <summary>
        /// Gets the collection of protocols.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.ProtocolCollection"/>.
        /// </value>
        public ProtocolCollection Protocols
        {
            get
            {
                if (this.protocols == null)
                {
                    this.protocols = new ProtocolCollection();
                    foreach (BtsProtocolType actualProtocol in this.catalogExplorer.ProtocolTypes)
                    {
                        Protocol configProtocol = Protocol.Load(this, actualProtocol.Name);
                        this.protocols.Add(configProtocol);
                    }
                }
                return this.protocols;
            }
        }

        /// <summary>
        /// Gets the collection of receive ports.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.ReceivePortCollection"/>.
        /// </value>
        public ReceivePortCollection ReceivePorts
        {
            get
            {
                if (this.receivePorts == null)
                {
                    this.receivePorts = new ReceivePortCollection();
                    foreach (BtsReceivePort actualPort in this.catalogExplorer.ReceivePorts)
                    {
                        ReceivePort configPort = ReceivePort.Load(this, actualPort.Name);
                        this.receivePorts.Add(configPort);
                    }
                }
                return this.receivePorts;
            }
        }

        /// <summary>
        /// Gets the collection of send port groups.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.SendPortGroupCollection"/>.
        /// </value>
        public SendPortGroupCollection SendPortGroups
        {
            get
            {
                if (this.sendPortGroups == null)
                {
                    this.sendPortGroups = new SendPortGroupCollection();
                    foreach (BtsSendPortGroup actualGroup in this.catalogExplorer.SendPortGroups)
                    {
                        SendPortGroup configGroup = SendPortGroup.Load(this, actualGroup.Name);
                        this.sendPortGroups.Add(configGroup);
                    }
                }
                return this.sendPortGroups;
            }
        }

        /// <summary>
        /// Gets the collection of send ports.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.SendPortCollection"/>.
        /// </value>
        public SendPortCollection SendPorts
        {
            get
            {
                if (this.sendPorts == null)
                {
                    this.sendPorts = new SendPortCollection();
                    foreach (BtsSendPort actualPort in this.catalogExplorer.SendPorts)
                    {
                        SendPort configPort = SendPort.Load(this, actualPort.Name);
                        this.sendPorts.Add(configPort);
                    }
                }
                return this.sendPorts;
            }
        }

        /// <summary>
        /// Gets the certificates installed in the AddressBook store of the local computer.
        /// </summary>
        public CertificateInfoCollection Certificates
        {
            get
            {
                if (this.certificates == null)
                {
                    this.certificates = new CertificateInfoCollection();
                    foreach (BtsCertificateInfo btsCertInfo in this.CatalogExplorer.Certificates)
                    {
                        CertificateInfo certInfo = CertificateInfo.Load(this, btsCertInfo.LongName);
                        this.certificates.Add(certInfo);
                    }
                }
                return this.certificates;
            }
        }

        /// <summary>
        /// Gets the collection of parties contained in the database.
        /// </summary>
        public PartyCollection Parties
        {
            get
            {
                if (this.parties == null)
                {
                    this.parties = new PartyCollection();
                    foreach (BtsParty btsParty in this.CatalogExplorer.Parties)
                    {
                        Party party = Party.Load(this, btsParty.Name);
                        this.parties.Add(party);
                    }
                }

                return this.parties;
            }
        }

        /// <summary>
        /// Gets or sets the logical name of the server that the BizTalk management database is on.
        /// </summary>
        /// <value>
        /// The logical name of the server that the BizTalk management database is on.
        /// </value>
        public string Server
        {
            get
            {
                return this.server;
            }

            set
            {
                this.server = value;
            }
        }

        /// <summary>
        /// Gets the catalog explorer for this installation.
        /// </summary>
        /// <value>
        /// The catalog explorer for this installation.
        /// </value>
        internal BtsCatalogExplorer CatalogExplorer
        {
            get
            {
                return this.catalogExplorer;
            }
        }

        #endregion

        #region Load Methods

        /// <summary>
        /// Loads the details of the local BizTalk installation using Windows Integrated security.
        /// </summary>
        /// <returns>
        /// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkInstallation"/>.
        /// </returns>
        /// <exception cref="System.InvalidOperationException">
        /// Thrown when the registry key detailing the local installation of BizTalk cannot be found.
        /// </exception>        
        public static BizTalkInstallation Load()
        {
            string server;
            string database;
            using (RegistryKey bizTalkAdminKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\BizTalk Server\3.0\Administration"))
            {
                if (bizTalkAdminKey == null)
                {
                    throw new InvalidOperationException("The local installation of BizTalk cannot be located.");
                }

                server = (string)bizTalkAdminKey.GetValue("MgmtDBServer", ".");
                database = (string)bizTalkAdminKey.GetValue("MgmtDBName", "BizTalkMgmtDb");
            }

            return BizTalkInstallation.Load(server, database);
        }

        /// <summary>
        /// Loads the details of a particular BizTalk installation using either SQL Server security or Windows
        /// Integrated security.
        /// </summary>
        /// <param name="server">
        /// The logical name of the server the BizTalk management database resides on.
        /// </param>
        /// <param name="database">
        /// The name of the BizTalk management database.
        /// </param>
        /// <returns>
        /// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkInstallation"/>.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown when either <paramref name="server"/> or <paramref name="database"/> are either <b>null</b>
        /// or zero length.
        /// </exception>
        public static BizTalkInstallation Load(string server, string database)
        {
            // check inputs
            if (string.IsNullOrEmpty(server))
            {
                throw new ArgumentNullException("server");
            }

            if (string.IsNullOrEmpty(database))
            {
                throw new ArgumentNullException("database");
            }

            // create a new installation and store basic properties
            BizTalkInstallation installation = new BizTalkInstallation();
            installation.server = server;
            installation.database = database;
            installation.catalogExplorer.ConnectionString = BizTalkInstallation.GetConnectionString(server, database);
            installation.groupName = installation.catalogExplorer.GroupName;

            // return the installation
            return installation;
        }

        #endregion

        #region public methods

        /// <summary>
        /// Cleans the installation of BizTalk Server
        /// </summary>
        public void Clean()
        {
            try
            {
                // orchestrations

                // Loop through all the assemblies looking for orchestrations
                foreach (BtsAssembly assembly in this.CatalogExplorer.Assemblies)
                {
                    // Loop through all the orchestrations
                    foreach (BtsOrchestration orchestration in assembly.Orchestrations)
                    {
                        // Ensure all instances are terminated
                        orchestration.AutoTerminateInstances = true;
                        
                        // Unenlist the orchestration
                        orchestration.Status = OrchestrationStatus.Unenlisted;

                        foreach (BtsOrchestrationPort port in orchestration.Ports)
                        {
                            // Remove all the mappings
                            port.ReceivePort = null;
                            port.SendPort = null;
                            port.SendPortGroup = null;
                        }
                    }
                }
                this.CatalogExplorer.SaveChanges();

                // Remove all receive ports until there are none left
                while (this.CatalogExplorer.ReceivePorts.Count > 0)
                {
                    // Get the next receive port
                    BtsReceivePort port = this.CatalogExplorer.ReceivePorts[0];

                    // Disable all the receive locations
                    foreach (BtsReceiveLocation location in port.ReceiveLocations)
                    {
                        location.Enable = false;
                    }

                    // Remove this receive port
                    this.CatalogExplorer.RemoveReceivePort(port);
                }
                this.CatalogExplorer.SaveChanges();

                // Remove all send port groups until there are none left
                while (this.CatalogExplorer.SendPortGroups.Count > 0)
                {
                    // Get the next send port group
                    BtsSendPortGroup port = this.CatalogExplorer.SendPortGroups[0];

                    // Stop and unenlist the port
                    port.Status = PortStatus.Bound;

                    // Remove all the send ports from this send port group
                    port.SendPorts.Clear();

                    // Remove this send port group
                    this.CatalogExplorer.RemoveSendPortGroup(port);
                }
                this.CatalogExplorer.SaveChanges();

                // Remove all send ports until there are none left
                while (this.CatalogExplorer.SendPorts.Count > 0)
                {
                    // Get the next send port
                    BtsSendPort port = this.CatalogExplorer.SendPorts[0];

                    // Stop and unenlist the port
                    port.Status = PortStatus.Bound;

                    // Remove this send port
                    this.CatalogExplorer.RemoveSendPort(port);
                }
                this.CatalogExplorer.SaveChanges();

                // removing all parties 
                while (this.CatalogExplorer.Parties.Count > 0)
                {
                    // Get the next send port
                    BtsParty party = this.CatalogExplorer.Parties[0];

                    this.CatalogExplorer.RemoveParty(party);
                }
                this.CatalogExplorer.SaveChanges();

                // undeploying all assemblies non system assemblies
                bool referencedAssemblyExists = false;
                for (int i = 0, j = this.CatalogExplorer.Assemblies.Count - 1; i <= j; i++)
                {
                    // getting the assembly
                    if (i == this.CatalogExplorer.Assemblies.Count)
                    {
                        break;
                    }

                    BtsAssembly assembly = this.CatalogExplorer.Assemblies[i];

                    if (!assembly.IsSystem)
                    {
                        try
                        {
                            BizTalkAssembly.Undeploy(
                            this,
                            assembly.Name,
                            new Version(assembly.Version),
                            assembly.Culture,
                            assembly.PublicKeyToken,
                            true,
                            null);

                            i--;
                            j--;

                            this.CatalogExplorer.Refresh();
                        }
                        catch (Exception ex)
                        {
                            System.Diagnostics.Trace.WriteLine("BizTalk 2004 undeploy exception: " + ex);
                            
                            // if ex.Source 
                            referencedAssemblyExists = true;
                        }
                    }

                    if (i == j && referencedAssemblyExists)
                    {
                        i = 0;
                        referencedAssemblyExists = false;
                    }
                }

                this.CatalogExplorer.SaveChanges();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("BizTalk 2004 exception: " + ex);
                this.CatalogExplorer.DiscardChanges();
                throw;
            }
        }
        #endregion
        
        #region Private Methods

        /// <summary>
        /// Gets a connection string a database using either SQL Server security or Windows Integrated security.
        /// </summary>
        /// <param name="server">
        /// The logical name of the server the database resides on.
        /// </param>
        /// <param name="database">
        /// The name of the database.
        /// </param>
        /// <returns>
        /// A connection string for the database
        /// </returns>
        private static string GetConnectionString(string server, string database)
        {
                return string.Format(
                    CultureInfo.InvariantCulture,
                    "Server={0};Database={1};Integrated Security=SSPI;",
                    server,
                    database);
            
        }

        #endregion
    }
}

